/* Declarations for bytecode operations.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef __KP_BYTECODE__
#define __KP_BYTECODE__   1

#include "defs.hpp"

KP_DECLS_BEGIN

enum
{
  OP_NOP,
  OP_DUP,
  OP_POP,
  OP_RET,
  OP_IS,
  OP_NOT,
  OP_CONS,
  OP_CAR,
  OP_CDR,
  OP_CADR,
  OP_APPLY,
  OP_TAPPLY,
  OP_LOADT,
  OP_LOADNIL,
  OP_LOAD0,
  OP_LOAD1,
  OP_LOADA0,
  OP_LOADA1,
  OP_LOADC00,
  OP_LOADC01,
  OP_MKCONT,
  OP_CLOSURE,
  OP_VFRAME,
  OP_TRYEND,
  OP_LDCALLER,
  OP_CLREXC,
  OP_SYMNAME,
  OP_SYMPKG,
  OP_COROVAL,
  OP_TYPEP,
  OP_TYPEP2,
  OP_RAISE,
  // Opcodes with long forms.
  OP_LOADI8,
  OP_LOADI32,
  OP_LOADCHR8,
  OP_LOADCHR32,
  OP_VARGC,
  OP_VARGCL,
  OP_JMP,
  OP_JMPL,
  OP_BRT,
  OP_BRTL,
  OP_BRN,
  OP_BRNL,
  OP_BRNEQ,
  OP_BRNEQL,
  OP_TCALL,
  OP_TCALLL,
  OP_CALL,
  OP_CALLL,
  OP_RECUR,
  OP_RECURL,
  OP_TRECUR,
  OP_TRECURL,
  OP_SETC0,
  OP_SETC0L,
  OP_SETC,
  OP_SETCL,
  OP_SETA,
  OP_SETAL,
  OP_SETG,
  OP_SETGL,
  OP_SETFGS,
  OP_SETFGSL,
  OP_LOADC0,
  OP_LOADC0L,
  OP_LOADC,
  OP_LOADCL,
  OP_LOADA,
  OP_LOADAL,
  OP_LOADG,
  OP_LOADGL,
  OP_LOADV,
  OP_LOADVL,
  OP_LOADX,
  OP_LOADXL,
  OP_LOADFGS,
  OP_LOADFGSL,
  OP_BIND,
  OP_BINDL,
  OP_TRYBEGIN,
  OP_TRYBEGINL,
  OP_SETAPOP,
  OP_SETAPOPL,
  OP_IRTJMP,
  OP_IRTJMPL,
  OP_OPTARGS,
  OP_OPTARGSL,
  OP_BRBOUND,
  OP_BRBOUNDL,
  OP_KWARGS,
  OP_KWARGSL,
  OP_JMPT,
  OP_JMPTL,
  OP_JMPN,
  OP_JMPNL,
  OP_BOX,
  OP_BOXL,
  OP_LOADB,
  OP_LOADBL,
  OP_SETB,
  OP_SETBL,
  OP_SKIP,
  OP_SKIPL,
  OP_UNBIND,
  OP_UNBINDL
};

struct bcode_instr
{
  uint16_t name_off;
  uint16_t flg_ex;

  static const int BC_FLG_FBIT = 2;
  static const int BC_OPS_MASK = (1 << BC_FLG_FBIT) - 1;

#define F_(n)   (1 << (BC_FLG_FBIT + n))

  static const int BC_CALL_FORM = F_(0);
  static const int BC_LOAD_FORM = F_(1);
  static const int BC_BRANCH_FORM = F_(2);
  static const int BC_LONG_FORM = F_(3);
  static const int BC_PURE_FORM = F_(4);

#undef F_

  int opcode () const;
  const char* name () const;

  int nops () const
    {
      return (this->flg_ex & BC_OPS_MASK);
    }

  bool branch_p () const
    {
      return ((this->flg_ex & BC_BRANCH_FORM) != 0);
    }

  bool long_p () const
    {
      return ((this->flg_ex & BC_LONG_FORM) != 0);
    }

  bool loadf_p () const
    {
      return ((this->flg_ex & BC_LOAD_FORM) != 0);
    }

  bool callf_p () const
    {
      return ((this->flg_ex & BC_CALL_FORM) != 0);
    }

  bool pure_p () const
    {
      return ((this->flg_ex & BC_PURE_FORM) != 0);
    }

  int argsize () const
    {
      return (this->long_p () ? sizeof (int32_t) :
           (this->branch_p () ? sizeof (int16_t) : 1));
    }

  uint32_t getuarg (const void *ptr) const
    {
      switch (this->argsize ())
        {
          case sizeof (uint32_t):
            return ((uint32_t)get32 (ptr));
          case sizeof (uint16_t):
            return ((uint16_t)get16 (ptr));
          default:
            return (*(uint8_t *)ptr);
        }
    }

  int getsarg (const void *ptr) const
    {
      switch (this->argsize ())
        {
          case sizeof (int32_t):
            return ((int32_t)get32 (ptr));
          case sizeof (int16_t):
            return ((int16_t)get16 (ptr));
          default:
            return (*(int8_t *)ptr);
        }
    }
};

KP_EXPORT const bcode_instr* bcode_get (int opc) __attribute__ ((pure));

inline const char* bcode_name (int opc)
{
  return (bcode_get(opc)->name ());
}

inline int
bcode_nops (int opc)
{
  return (bcode_get(opc)->nops ());
}

inline bool
bcode_branch_p (int opc)
{
  return (bcode_get(opc)->branch_p ());
}

inline bool
bcode_long_p (int opc)
{
  return (bcode_get(opc)->long_p ());
}

KP_DECLS_END

#endif
